#include "Kernel.h"
#include "Utils/List.h"
#include "Utils/String.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/Process.h"
#include "Scheduler/Thread.h"
#include "Hal/IHardware.h"
#include "Hal/ITaskContext.h"

enum
{
	THREAD_STACK_SIZE = 1024,
};


Thread::Thread( const char * name, ThreadFunction function, void * argument )
{
	IF_PARANOID( this->signature = THREAD_SIGNATURE );

	this->process = Scheduler::CurrentProcess;
	this->name = StringCopy( name );
	this->state = THREAD_CREATED;
	this->function = function;
	this->argument = argument;
	this->result = 0;
	this->stack = (uint32 *)sHeap.Allocate( THREAD_STACK_SIZE );
	this->stackSize = THREAD_STACK_SIZE;
	this->context = sHardware->CreateContext( this->stack, this->stackSize, (TaskEntry)&Thread::StartThread, this );
}


Thread::Thread( const char * name, void * stack, Size stackSize )
{
	IF_PARANOID( this->signature = THREAD_SIGNATURE );

	this->process = Scheduler::CurrentProcess;
	this->name = StringCopy( name );
	this->state = THREAD_CREATED;
	this->function = function;
	this->argument = argument;
	this->result = 0;
	this->stack = (uint32 *)stack;
	this->stackSize = stackSize;
	this->context = sHardware->CreateEmptyContext();
}


Thread::~Thread()
{
	PARANOID_ASSERT( this->signature == THREAD_SIGNATURE );
	IF_PARANOID( this->signature = NULL );

	process = NULL;
	delete name;
	state = THREAD_DESTROYED;
	function = NULL;
	argument = NULL;
	result = 0;

	// don't delete the kernel stack
	if( this != Scheduler::SystemThread )
		delete stack;

	if( context != NULL )
	{
		sHardware->DisposeContext( context );
		context = NULL;
	}
}



void Thread::StartThread( Thread * thread )
{
	ASSERT( thread != NULL );
	PARANOID_ASSERT( thread->signature == THREAD_SIGNATURE );

	SCHEDULER_TRACE( "Thread %p \"%s\" started...\n", thread, thread->GetName() );

	thread->state = THREAD_RUNNING;
	thread->result = thread->function( thread->argument );

	SCHEDULER_TRACE( "Thread %p \"%s\" exited...\n", thread, thread->GetName() );

	thread->state = THREAD_FINISHED;

	// idle until we are taken care of
	for(;;)
		Scheduler::Yield();
}
